package cn.chiship.framework.common.config.interceptor;

import cn.chiship.framework.common.service.GlobalCacheService;
import cn.chiship.framework.common.util.FrameworkUtil2;
import cn.chiship.sdk.cache.service.RedisService;
import cn.chiship.sdk.cache.service.UserCacheService;
import cn.chiship.sdk.core.annotation.Authorization;
import cn.chiship.sdk.core.annotation.NoParamsSign;
import cn.chiship.sdk.core.annotation.NoVerificationAppIdAndKey;
import cn.chiship.sdk.core.annotation.NoVerificationProjectId;
import cn.chiship.sdk.core.base.BaseResult;
import cn.chiship.sdk.core.base.constants.BaseCacheConstants;
import cn.chiship.sdk.core.base.constants.BaseConstants;
import cn.chiship.sdk.core.enums.BaseResultEnum;
import cn.chiship.sdk.core.encryption.EncryptionUtil;
import cn.chiship.sdk.core.encryption.ProofUtil;
import cn.chiship.sdk.core.enums.HeaderEnum;
import cn.chiship.sdk.core.id.SnowflakeIdUtil;
import cn.chiship.sdk.core.util.StringUtil;
import cn.chiship.sdk.core.util.http.ResponseUtil;
import cn.chiship.sdk.core.util.ip.IpUtils;
import com.alibaba.fastjson.JSONObject;
import cn.chiship.framework.common.annotation.SystemOptionAnnotation;
import cn.chiship.framework.common.constants.CommonCacheConstants;
import com.google.common.base.Strings;
import cn.chiship.framework.common.util.UserPermissionsUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * 自定义拦截器，对请求进行身份验证 2017-11-03
 *
 * @author lijian
 */
public class AuthorizationInterceptor implements HandlerInterceptor {

	private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationInterceptor.class);

	private static final String PROF_PROJECT_ID = "projectId";

	private static final String PROF_APP_SECRET = "appSecret";

	private static final String PREFIX_MD5 = "MD5=";

	private static final int MD5_PREFIEX_ENCRYPT_LEGTH = 4;

	private UserCacheService userCacheService;

	private RedisService redisService;

	private String publicKey = null;

	private String privateKey = null;

	/**
	 * 鉴权信息的无用前缀，默认为空
	 */
	private String httpHeaderPrefix = "";

	private String encryptionCode = "";

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
		if (response.getStatus() == BaseConstants.STATUS_NOT_FOUND) {
			return writeError(response, BaseResult.error(BaseResultEnum.NOT_FOUND, null));
		}
		/**
		 * 如果不是映射到方法直接通过
		 */
		if (!(handler instanceof HandlerMethod)) {
			return true;
		}
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		Method method = handlerMethod.getMethod();
		if (FrameworkUtil2.getChishipDefaultProperties().getDemoEnabled()
				&& BaseConstants.HTTP_REQUEST_TYPE_POST.equals(request.getMethod())) {
			String whiteList = FrameworkUtil2.getChishipDefaultProperties().getInterfaceWhite();
			if (StringUtil.isNullOrEmpty(whiteList)) {
				return writeError(response,
						BaseResult.error(BaseResultEnum.SYSTEM_ERROR, "演示环境不能操作，如需了解请加入QQ群：1002858707!"));
			}
			if (!StringUtil.strToListString(whiteList, ",").contains(method.getName())) {
				return writeError(response,
						BaseResult.error(BaseResultEnum.SYSTEM_ERROR, "演示环境不能操作，如需了解请加入QQ群：1002858707!"));
			}
		}
		redisService = factory.getBean(RedisService.class);
		GlobalCacheService globalCacheService = factory.getBean(GlobalCacheService.class);
		String ip = IpUtils.getIpAddr(request);
		BaseResult baseResult = globalCacheService.validateLoginIp(ip);
		if (!baseResult.isSuccess()) {
			return writeError(response, baseResult);
		}

		if (Boolean.FALSE.equals(verificationAppIdAndKey(request, response, handlerMethod))) {
			return false;
		}
		if (Boolean.FALSE.equals(verificationParamsSign(request, response, handlerMethod))) {
			return false;
		}
		if (Boolean.FALSE.equals(verificationAuthorization(request, response, handlerMethod, factory))) {
			return false;
		}

		/**
		 * 为了解决统一拦截异常信息保存日志
		 */
		SystemOptionAnnotation systemOption = method.getAnnotation(SystemOptionAnnotation.class);
		if (systemOption != null) {
			request.setAttribute("systemOption", systemOption);
		}
		request.setAttribute("methodName", method.getDeclaringClass().getName() + "." + method.getName() + "()");
		request.setAttribute("ip", IpUtils.getIpAddr(request));
		request.setAttribute("requestId", SnowflakeIdUtil.generateId());

		/**
		 * 检测用户是否拥有权限或角色
		 */

		return UserPermissionsUtil.checkOption(method, userCacheService, request);
	}

	/**
	 * 验证App-Id 和App-Key是否有效 如果验证App-Id和App-Key失败，并且方法注明了NoAppKeyAndSecret 不开启验证
	 * 查看方法上是否有注解 查看方法所在的Controller是否有注解
	 */
	public boolean verificationAppIdAndKey(HttpServletRequest request, HttpServletResponse response,
			HandlerMethod handlerMethod) throws Exception {
		Method method = handlerMethod.getMethod();
		/**
		 * 验证ProjectId
		 */
		Boolean isVerificationProjectId = false;
		String projectId = null;
		if (method.getAnnotation(NoVerificationProjectId.class) == null
				&& handlerMethod.getBeanType().getAnnotation(NoVerificationProjectId.class) == null) {
			isVerificationProjectId = true;
			projectId = request.getHeader(HeaderEnum.HEADER_PROJECTS_ID.getName());
			if (Strings.isNullOrEmpty(projectId)) {
				projectId = request.getParameter(HeaderEnum.HEADER_PROJECTS_ID.getName());
				if (Strings.isNullOrEmpty(projectId)) {
					return writeError(response, BaseResult.error(BaseResultEnum.HEADER_NO_PROJECTS_ID_PARAM, null));
				}
			}
		}
		if (method.getAnnotation(NoVerificationAppIdAndKey.class) == null
				&& handlerMethod.getBeanType().getAnnotation(NoVerificationAppIdAndKey.class) == null) {
			String appId = request.getHeader(HeaderEnum.HEADER_APP_ID.getName());
			String appKey = request.getHeader(HeaderEnum.HEADER_APP_KEY.getName());
			if (Strings.isNullOrEmpty(appKey) || Strings.isNullOrEmpty(appId)) {
				return writeError(response, BaseResult.error(BaseResultEnum.HEADER_NO_APP_KEY_AND_APP_ID, null));
			}
			else {
				JSONObject prof = (JSONObject) redisService
						.get(CommonCacheConstants.buildKey(BaseCacheConstants.REDIS_PROOF_PREFIX));
				if (prof != null) {
					if (Boolean.TRUE.equals(isVerificationProjectId)
							&& !projectId.equals(prof.getString(PROF_PROJECT_ID))) {
						return writeError(response, BaseResult.error(BaseResultEnum.ERROR_PROJECTS_ID, null));
					}

					/**
					 * 随机安全码
					 */
					encryptionCode = prof.getString("signKey");

					/**
					 * 公钥
					 */
					publicKey = prof.getString("publicKey");

					/**
					 * 私钥
					 */
					privateKey = prof.getString("privateKey");

					/**
					 * 通过消息头获得AppId和AppKey 反向解密变成appSecret
					 */
					if (!ProofUtil.verifyAppSecret(appId, appKey, encryptionCode, prof.getString(PROF_APP_SECRET))) {
						return writeError(response, BaseResult.error(BaseResultEnum.ERROR_APP_KEY, null));
					}
				}
				else {
					return writeError(response, BaseResult.error(BaseResultEnum.ERROR_APP_ID, null));
				}
			}
		}
		return true;
	}

	/**
	 * 验证参数签名 如果验证Sign失败，并且方法注明了NoEncryption 不开启加密验签 查看方法上是否有注解 查看方法所在的Controller是否有注解
	 */
	public boolean verificationParamsSign(HttpServletRequest request, HttpServletResponse response,
			HandlerMethod handlerMethod) throws Exception {
		Method method = handlerMethod.getMethod();
		if (method.getAnnotation(NoParamsSign.class) == null
				&& handlerMethod.getBeanType().getAnnotation(NoParamsSign.class) == null) {
			String encrypt = request.getHeader(HeaderEnum.HEADER_SIGN.getName());
			if (!Strings.isNullOrEmpty(encrypt)) {
				if (!PREFIX_MD5.equals(encrypt.substring(0, MD5_PREFIEX_ENCRYPT_LEGTH))) {
					boolean signIsPass = EncryptionUtil.rsaVerifySignature(request, encryptionCode, publicKey);
					if (!signIsPass) {
						return writeError(response, BaseResult.error(BaseResultEnum.SIGN_ERR, null));
					}
				}

			}
			else {
				return writeError(response, BaseResult.error(BaseResultEnum.HEADER_NO_SIGN_PARAM, null));
			}
		}
		return true;
	}

	/**
	 * 校验用户是否登录 如果验证token失败，并且方法注明了Authorization 查看方法上是否有注解 查看方法所在的Controller是否有注解
	 */
	public boolean verificationAuthorization(HttpServletRequest request, HttpServletResponse response,
			HandlerMethod handlerMethod, BeanFactory factory) throws Exception {
		Method method = handlerMethod.getMethod();

		if (method.getAnnotation(Authorization.class) != null
				|| handlerMethod.getBeanType().getAnnotation(Authorization.class) != null) {
			/**
			 * 从header中得到token
			 */
			String token = request.getHeader(HeaderEnum.HEADER_ACCESS_TOKEN.getName());
			if (token != null && token.startsWith(httpHeaderPrefix) && token.length() > 0) {
				userCacheService = factory.getBean(UserCacheService.class);
				if (!userCacheService.isValid()) {
					return writeError(response, BaseResult.error(BaseResultEnum.ERROR_TOKEN, null));
				}
			}
			else {
				return writeError(response, BaseResult.error(BaseResultEnum.HEADER_NO_TOKEN, null));
			}
		}
		return true;
	}

	public boolean writeError(HttpServletResponse response, BaseResult baseResult) throws IOException {
		ResponseUtil.writeJson(response, baseResult);
		return false;
	}

}
